MOOC Utils

Utils like auto assignment solver extension and course notice announcment notification for MOOC courses

Authentication system

Introduction#

OTP login with JWT cookies and session handling. Security choices, failure modes, and the HTTP surface clients call.

Project structure#

The authentication system spans two primary parts:

  • Backend (Python/FastAPI): authentication routes, token management, persistence, and email delivery
  • Frontend (Next.js): authentication context, UI flow, and API client integration

Core components#

  • Authentication Router: exposes endpoints for OTP request, verification, token refresh, logout, and profile retrieval
  • AuthService: orchestrates OTP generation/validation, JWT creation/verification, refresh token lifecycle, and user provisioning
  • Models: User, OTP, and RefreshToken for persistence
  • Schemas: Pydantic models for request/response contracts
  • Email Service: sends OTP via console or SMTP
  • Frontend Auth Context: manages session state, cookies, and navigation
  • API Client: centralized fetch wrapper with credential handling

Architecture overview#

The authentication flow integrates frontend and backend components with secure cookie-based sessions using JWTs.

Detailed component analysis#

Backend authentication router#

  • Routes:
  • POST /auth/request-otp: generates and emails OTP; returns whether user is new and expiry time
  • POST /auth/verify-otp: validates OTP, creates access/refresh tokens, sets secure cookies
  • POST /auth/refresh: rotates refresh token and issues new access/refresh cookies
  • POST /auth/logout: revokes refresh token and clears cookies
  • GET /auth/me: protected route returning current user via access token cookie
  • Cookie policy:
  • access_token: HttpOnly, SameSite=Lax, secure unless debug, path “/”
  • refresh_token: HttpOnly, SameSite=Lax, secure unless debug, path “/”
  • Error handling:
  • Returns HTTP 400/401 with descriptive messages for invalid/expired OTP or missing/invalid tokens

Authentication service implementation#

Responsibilities:

  • OTP lifecycle: generation, persistence, expiry enforcement, and usage marking
  • User provisioning: auto-create user on first login
  • JWT lifecycle: encode/decode access tokens, manage refresh tokens with rotation and revocation
  • Token persistence: store refresh tokens with expiry and revoked flag
  • Email delivery: console or SMTP transport

Key behaviors:

  • Access token payload includes subject, email, issued-at, and expiry
  • Refresh token rotation invalidates previous token and issues a new one
  • OTP uniqueness per email and latest-first validation keep codes fresh

Data models#

  • User: identifier, email, optional name/telegram, activity flag, timestamps
  • OtpCode: email, code, expiry, usage flag, timestamps
  • RefreshToken: foreign key to User, unique token, expiry, revoked flag, timestamps
  • Access token cookie:
  • Name: access_token
  • Attributes: HttpOnly, SameSite=Lax, secure unless debug, path “/”
  • Max age: derived from settings
  • Refresh token cookie:
  • Name: refresh_token
  • Attributes: HttpOnly, SameSite=Lax, secure unless debug, path “/”
  • Max age: derived from settings
  • Session retrieval:
  • Protected route middleware reads access_token cookie, verifies JWT, loads user by sub claim
  • Token refresh:
  • Uses refresh_token cookie to validate, revoke old, issue new refresh token, and update cookies

Password hashing strategy#

  • The system does not hash passwords; authentication relies on OTP delivery and JWT-based session management
  • No password field exists in the User model

Frontend authentication flow#

  • Auth Context:
  • Loads session on startup via /auth/me
  • Provides requestOtp, verifyOtp, refresh, and logout
  • Persists user state and navigates after successful login
  • Login Page:
  • Two-step UX: email → code
  • Zod-based validation for email and 6-digit code
  • Conditional rendering and error messaging
  • API Client:
  • Fetch wrapper with credentials: include
  • Centralized endpoints for auth operations
  • Typed responses aligned with backend schemas

Dependency analysis#

  • Router depends on:
  • AuthService for OTP, token, and user operations
  • Settings for cookie lifetimes and delivery configuration
  • AuthService depends on:
  • Models for persistence
  • OtpEmailService for delivery
  • Settings for cryptographic and timing parameters
  • Frontend depends on:
  • Auth Context for state management
  • API Client for network requests
  • Types for type safety

Performance considerations#

  • OTP generation uses constant-time numeric codes; consider rate limiting per email to prevent abuse
  • Token rotation creates new refresh tokens; ensure database indexing on token and user fields remains efficient
  • Access token expiry is short-lived by default; balance usability with security
  • Email delivery is synchronous; consider queuing for production workloads

Troubleshooting guide#

Common issues and resolutions:

  • Invalid or expired OTP:
  • Cause: OTP not found, expired, or already used
  • Resolution: Trigger new OTP request; ensure clock sync and correct email
  • Missing or invalid access token:
  • Cause: Missing cookie, expired token, or invalid signature
  • Resolution: Re-authenticate; verify JWT secret and clock
  • Missing refresh token:
  • Cause: Not present or revoked/expired
  • Resolution: Perform full OTP login; avoid long-lived sessions
  • Cookie not set:
  • Cause: SameSite/secure flags mismatch with deployment
  • Resolution: Adjust settings for local vs. production environments
  • SMTP configuration errors:
  • Cause: Missing SMTP settings for non-console delivery
  • Resolution: Provide required SMTP environment variables

Conclusion#

OTP expiry and cookie flags matter more than clever token formats. Misconfigured CORS shows up as silent login failures in the browser.

Appendices#

API endpoint specifications#

  • POST /auth/request-otp

  • Request: { email: string }

  • Response: { message: string, is_new_user: boolean, expires_at: datetime }

  • Description: Generates OTP and delivers it; returns expiry time and new-user flag

  • POST /auth/verify-otp

  • Request: { email: string, code: string }

  • Response: { user: UserResponse, is_new_user: boolean }

  • Cookies: Sets access_token and refresh_token

  • Description: Validates OTP, provisions user if needed, issues tokens

  • POST /auth/refresh

  • Request: none (uses refresh_token cookie)

  • Response: { user: UserResponse, is_new_user: boolean }

  • Cookies: Rotates refresh token and updates access token cookie

  • Description: Refreshes session using valid refresh token

  • POST /auth/logout

  • Request: none (uses refresh_token cookie)

  • Response: 204 No Content

  • Description: Revokes refresh token and clears cookies

  • GET /auth/me

  • Request: none (uses access_token cookie)

  • Response: UserResponse

  • Description: Returns currently authenticated user

Security considerations:

  • Cookies are HttpOnly and use SameSite=Lax; secure flag is disabled only in debug mode
  • Access tokens are signed HS256; keep jwt_secret secret
  • Refresh tokens are rotated on each refresh and stored with expiry/revocation flags

Client integration examples#

  • Next.js usage pattern:

  • Wrap app with AuthProvider

  • Use useAuth hook to call requestOtp, verifyOtp, refresh, logout

  • Navigate based on isAuthenticated and loading state

  • API client usage:

  • requestOtp(email) → OtpRequestResponse

  • verifyOtp(email, code) → AuthStatus

  • refreshSession() → AuthStatus

  • logout() → void

  • getMe() → User